package com.zengdw.logRecord.resolve;

import com.zengdw.logRecord.domain.LogOperations;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.CachedExpressionEvaluator;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.TypedValue;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 解析核心类
 *
 * @author zengd
 * @date 2021/9/18 14:20
 */
public class LogRecordValueParser extends CachedExpressionEvaluator {
    private final ParseFunctionFactory parseFunctionFactory;

    private final Pattern pattern = Pattern.compile("\\{(.*?)\\(&\\{(.*?)}\\)}|&\\{(.*?)}");
    private final Map<ExpressionKey, Expression> expressionCache = new ConcurrentHashMap<>(64);

    public LogRecordValueParser(ParseFunctionFactory parseFunctionFactory) {
        this.parseFunctionFactory = parseFunctionFactory;
    }

    private AnnotatedElementKey createKey(Class<?> targetClass, Method method) {
        return new AnnotatedElementKey(method, targetClass);
    }

    public EvaluationContext createEvaluationContext(Method method, Object[] args,
                                                     Object ret, String errorMsg) {
        DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
        return new LogRecordEvaluationContext(TypedValue.NULL, method, args, discoverer, ret, errorMsg);
    }

    /**
     * 解析日志模板
     */
    public void resolve(LogOperations operations, Class<?> targetClass, Method method, EvaluationContext context, boolean before) {
        Matcher matcher = pattern.matcher(operations.getTemplate());
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String functionName = matcher.group(1);
            String paramsSpElTemplate = matcher.group(2);
            String spElTemplate = matcher.group(3);
            boolean beforeFunction = parseFunctionFactory.isBeforeFunction(functionName);
            if (before && beforeFunction) {
                doExecute(targetClass, method, context, matcher, sb, functionName, paramsSpElTemplate);
            } else if (!beforeFunction && StringUtils.hasText(functionName)) {
                doExecute(targetClass, method, context, matcher, sb, functionName, paramsSpElTemplate);
            }
            if (StringUtils.hasText(spElTemplate)) {
                // 返回值和异常信息在方法执行前不解析
                boolean b = ("#_ret".equals(spElTemplate) || "#_errorMsg".equals(spElTemplate)) && before;
                if(b) {
                    continue;
                }
                AnnotatedElementKey elementKey = createKey(targetClass, method);
                String value = getExpression(expressionCache, elementKey, spElTemplate).getValue(context, String.class);
                matcher.appendReplacement(sb, value == null ? "" : value);
            }
        }
        operations.setTemplate(matcher.appendTail(sb).toString());
    }

    private void doExecute(Class<?> targetClass, Method method, EvaluationContext context, Matcher matcher, StringBuffer sb, String functionName, String spElTemplates) {
        AnnotatedElementKey elementKey = createKey(targetClass, method);
        String value = getExpression(expressionCache, elementKey, spElTemplates).getValue(context, String.class);
        value = parseFunctionFactory.apply(functionName, value);
        matcher.appendReplacement(sb, value);
    }

}
